﻿'/*
' ***** BEGIN LICENSE BLOCK *****
' * The contents of this file are subject to the Mozilla Public License
' * Version 1.1 (the "License"); you may not use this file except in
' * compliance with the License. You may obtain a copy of the License at
' * http://www.mozilla.org/MPL/
' *
' * Software distributed under the License is distributed on an "AS IS"
' * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
' * License for the specific language governing rights and limitations
' * under the License.
' *
' * The Original Code is BS1.
' *
' * The Initial Developer of the Original Code is SpaceMan.
' * Portions created by SpaceMan are Copyright (C) 2009
' * SpaceMan. All Rights Reserved.
' *
' * Contributor(s):
' * SpaceMan <sandaimespaceman@gmail.com>
' *
' * Alternatively, the contents of this file may be used under the terms
' * of the GNU Public License (the "GPL License"), in which case the
' * provisions of MPL License are applicable instead of those
' * above. If you wish to allow use of your version of this file only
' * under the terms of the GPL License and not to allow others to use
' * your version of this file under the MPL, indicate your decision by
' * deleting the provisions above and replace them with the notice and
' * other provisions required by the GPL License. If you do not delete
' * the provisions above, a recipient may use your version of this file
' * under either the MPL or the GPL License.
' ***** END LICENSE BLOCK *****
' */

Imports System.IO

''' <summary>
''' The main BrainSpace 1 execution context.
''' </summary>
Public Class BrainSpace1
#Region "Properties"
    ''' <summary>
    ''' The raw source code.
    ''' </summary>
    Public RawCode As String = " "
    Private Width As Integer = 1
    ''' <summary>
    ''' The current opcode to be executed.
    ''' </summary>
    Public CodeChar As String = " "
    ''' <summary>
    ''' The source code in interpreter-readable format
    ''' </summary>
    Public Code As New List(Of String)
    ''' <summary>
    ''' The direction of the code. Not to be changed unless for debug propuse.
    ''' </summary>
    Public Dir As Direction = Direction.Right
    ''' <summary>
    ''' The X location of the code. Not to be changed unless for debug propuse.
    ''' </summary>
    Public LocationX As Integer = 0
    ''' <summary>
    ''' The Y location of the code. Not to be changed unless for debug propuse.
    ''' </summary>
    Public LocationY As Integer = 0
    ''' <summary>
    ''' The memory cells. Not to be changed unless for debug propuse.
    ''' </summary>
    Public Memory As New List(Of Integer)
    ''' <summary>
    ''' The current memory pointer. Not to be changed unless for debug propuse.
    ''' </summary>
    Public MemoryPointer As Integer = 0
    ''' <summary>
    ''' The input/output handler. It contains callbacks for input and output.
    ''' </summary>
    Public Handler As BS1Handler
#End Region
    ''' <summary>
    ''' The four directions. They are used in the middle of the execution.
    ''' </summary>
    ''' 
    Public Enum Direction
        Up = 0
        Right = 1
        Down = 2
        Left = 3
    End Enum
#Region "Methods"
    ''' <summary>
    ''' The constructor of BrainSpace1 execution context.
    ''' </summary>
    ''' <param name="_code">The code to be executed.</param>
    ''' <param name="_handler">The input/output handler.</param>
    Public Sub New(ByVal _Code As String, ByVal _Handler As BS1Handler)
        ' Process the code
        SetCode(_Code)
        ' Set the handler
        Handler = _Handler
    End Sub

    ''' <summary>
    ''' Rebuild the BS1 code
    ''' </summary>
    ''' <param name="_Code">The code to be rebuilt.</param>
    Public Sub SetCode(ByVal _Code As String)
        ' Process the code
        RawCode = _Code
        Dim C() As String = {""}
        C = _Code.Split(vbNewLine)
        ' Get max width.
        For i As Integer = 0 To C.Length - 1 Step 1
            ' Update max width
            If C(i).Length > Width Then
                Width = C(i).Length
            End If
            ' Add code
            Code.Add(C(i))
        Next
        Code.Item(0) = vbLf + Code.Item(0)
    End Sub

    ''' <summary>
    ''' Run for one step. This way is safer because you can prompt user
    ''' to end the program in case of infinite loop.
    ''' </summary>
    ''' <returns>TRUE for continue execution or FALSE to end program.</returns>
    Public Function Go() As Boolean
        ' Get current opcode
        Try
            CodeChar = Code.Item(LocationY).Chars(LocationX + 1)
        Catch e As ArgumentOutOfRangeException
            CodeChar = " "
        End Try

        Select Case CodeChar
            Case "^"
                Dir = Direction.Up
            Case "V"
                Dir = Direction.Down
            Case "v"
                Dir = Direction.Down
            Case "<"
                Dir = Direction.Left
            Case ">"
                Dir = Direction.Right
            Case "\"
                Select Case Dir
                    Case Direction.Up
                        Dir = Direction.Left
                    Case Direction.Right
                        Dir = Direction.Down
                    Case Direction.Down
                        Dir = Direction.Right
                    Case Direction.Left
                        Dir = Direction.Up
                    Case Else
                        Dir = Direction.Right
                End Select
            Case "/"
                Select Case Dir
                    Case Direction.Up
                        Dir = Direction.Right
                    Case Direction.Right
                        Dir = Direction.Up
                    Case Direction.Down
                        Dir = Direction.Left
                    Case Direction.Left
                        Dir = Direction.Down
                    Case Else
                        Dir = Direction.Right
                End Select
            Case "%"
                Select Case Dir
                    Case Direction.Up
                        Dir = Direction.Down
                    Case Direction.Right
                        Dir = Direction.Left
                    Case Direction.Down
                        Dir = Direction.Up
                    Case Direction.Left
                        Dir = Direction.Right
                    Case Else
                        Dir = Direction.Right
                End Select
            Case "?"
                Try
                    If Memory(MemoryPointer) <> 0 Then
                        Select Case Dir
                            Case Direction.Up
                                Dir = Direction.Down
                            Case Direction.Right
                                Dir = Direction.Left
                            Case Direction.Down
                                Dir = Direction.Up
                            Case Direction.Left
                                Dir = Direction.Right
                            Case Else
                                Dir = Direction.Right
                        End Select
                    End If
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(0)
                End Try
            Case "*"
                Dim Skip As Integer = 1
                Try
                    If Memory.Item(MemoryPointer) <> 0 Then
                        Skip = 0
                    End If
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(0)
                End Try

                Select Case Dir
                    Case Direction.Up
                        LocationX -= Skip
                    Case Direction.Right
                        LocationX += Skip
                    Case Direction.Down
                        LocationY += Skip
                    Case Direction.Left
                        LocationX -= Skip
                End Select
            Case "x"
                Return False
            Case "X"
                Return False
            Case "l"
                If MemoryPointer > 0 Then
                    MemoryPointer -= 1
                End If
            Case "L"
                If MemoryPointer > 0 Then
                    MemoryPointer -= 1
                End If
            Case "{"
                If MemoryPointer > 0 Then
                    MemoryPointer -= 1
                End If
            Case "r"
                MemoryPointer += 1
            Case "R"
                MemoryPointer += 1
            Case "}"
                MemoryPointer += 1
            Case "+"
                Try
                    Memory.Item(MemoryPointer) += 1
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(1)
                End Try
            Case "p"
                Try
                    Memory.Item(MemoryPointer) += 1
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(1)
                End Try
            Case "P"
                Try
                    Memory.Item(MemoryPointer) += 1
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(1)
                End Try
            Case "-"
                Try
                    Memory.Item(MemoryPointer) -= 1
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(-1)
                End Try
            Case "m"
                Try
                    Memory.Item(MemoryPointer) -= 1
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(-1)
                End Try
            Case "M"
                Try
                    Memory.Item(MemoryPointer) -= 1
                Catch e As ArgumentOutOfRangeException
                    Memory.Add(-1)
                End Try
            Case "o"
                Handler.OutputCallback(Memory.Item(MemoryPointer))
            Case "O"
                Handler.OutputCallback(Memory.Item(MemoryPointer))
            Case "0"
                Handler.OutputCallback(Memory.Item(MemoryPointer))
            Case "i"
            Case "I"
            Case "1"
                Dim Input As String = Handler.InputCallback()
                Memory.Item(MemoryPointer) = Asc(Input.Chars(0))
        End Select

        Select Case Dir
            Case Direction.Up
                If LocationY > 0 Then
                    LocationY -= 1
                Else
                    Return False
                End If
            Case Direction.Right
                If LocationX <= Width Then
                    LocationX += 1
                Else
                    Return False
                End If
            Case Direction.Down
                If LocationY <= Height() Then
                    LocationY += 1
                Else
                    Return False
                End If
            Case Direction.Left
                If LocationX > 0 Then
                    LocationX -= 1
                Else
                    Return False
                End If
        End Select
        Return True
    End Function
    ''' <summary>
    ''' Run the code from begin to end. If you want to debug the program, use Go() instead of Run().
    ''' This method is also unsafe for infinite loops.
    ''' </summary>
    Public Sub Run()
        While True
            If Not Go() Then
                Exit While
            End If
        End While
    End Sub
    ''' <summary>
    ''' Get height of the code
    ''' </summary>
    ''' <returns>The height of the code</returns>
    Public Function Height()
        Return Code.Count
    End Function
    ''' <summary>
    ''' Get content from a file
    ''' </summary>
    ''' <param name="FullPath">Path to the file</param>
    ''' <returns>The content of the file</returns>
    ''' <exception cref="IOException" />
    Public Shared Function FileGetContents(ByVal FullPath As String) As String
        Dim Contents As String
        Dim Reader As StreamReader
        Reader = New StreamReader(FullPath)
        Contents = Reader.ReadToEnd()
        Reader.Close()
        Return Contents
    End Function
#End Region

End Class
